// <copyright project="Salient.SqlServer" file="HierarchyId.cs" company="Sky Sanders">
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.   
// Attribution is appreciated
// </copyright> 
// <version>1.0</version>
using System;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Types;

namespace Salient.SqlServer.Types
{
    /// <summary>
    /// This class is broken. Am working on a serializable wrapper
    /// for use with NHibernate - but have switched focus back to
    /// trying to get the native object to work. This is an extreeme
    /// workaround that I would prefer to avoid.
    /// </summary>
    [Serializable]
    [TypeConverter(typeof (HierarchyIdTypeConverter))]
    public class HierarchyId : INullable, IComparable
    {
        private static readonly HierarchyId sm_null;

        private SqlHierarchyId _id;

        static HierarchyId()
        {
            sm_null = new HierarchyId();
        }

        public HierarchyId()
        {
        }

        public HierarchyId(String id)
        {
            _id = SqlHierarchyId.Parse(id);
        }

        public HierarchyId(SqlHierarchyId id)
        {
            _id = id;
        }

        public SqlHierarchyId Inner
        {
            get { return _id; }
        }

        public static HierarchyId Null
        {
            get { return sm_null; }
        }

        #region IComparable Members

        public int CompareTo(object obj)
        {
            if (!(obj is HierarchyId))
            {
                throw new ArgumentException(
                    /*string.Format(ordpathres.Culture, ordpathres.ArgumentNotSqlHierarchyId, new object[0])*/);
            }
            return Inner.CompareTo(((HierarchyId) obj).Inner);
        }

        #endregion

        #region INullable Members

        public bool IsNull
        {
            get { return (_id.IsNull); }
        }

        #endregion

        public static HierarchyId GetRoot()
        {
            SqlHierarchyId root = SqlHierarchyId.GetRoot();
            var id = new HierarchyId(root);
            return id;
        }


        public HierarchyId GetDescendant(HierarchyId child1, HierarchyId child2)
        {
            if (IsNull)
            {
                return Null;
            }

            return new HierarchyId(_id.GetDescendant(child1.Inner, child2.Inner));
        }


        public SqlBoolean IsDescendantOf(HierarchyId parent)
        {
            if (!IsNull && !parent.IsNull)
            {
                return _id.IsDescendantOf(parent.Inner);
            }

            return SqlBoolean.Null;
        }


        public SqlInt16 GetLevel()
        {
            if (IsNull)
            {
                return SqlInt16.Null;
            }
            return (short) _id.GetLevel();
        }


        public HierarchyId GetAncestor(int n)
        {
            if (IsNull)
            {
                return Null;
            }
            return new HierarchyId(_id.GetAncestor(n));
        }


        public HierarchyId GetReparentedValue(HierarchyId oldRoot, HierarchyId newRoot)
        {
            if ((IsNull || oldRoot.IsNull) || newRoot.IsNull)
            {
                return Null;
            }
            return new HierarchyId(_id.GetReparentedValue(oldRoot.Inner, newRoot.Inner));
        }

        public static HierarchyId Parse(SqlString input)
        {
            if (input.IsNull)
            {
                return Null;
            }
            return new HierarchyId(SqlHierarchyId.Parse(input));
        }

        public int CompareTo(HierarchyId hid)
        {
            return Inner.CompareTo(hid.Inner);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is HierarchyId) && !(obj is SqlHierarchyId))
            {
                return false;
            }
            return obj.ToString() == ToString();
        }

        public override int GetHashCode()
        {
            return Inner.GetHashCode();
        }


        public override string ToString()
        {
            return Inner.ToString();
        }

        public void Read(BinaryReader r)
        {
            _id.Read(r);
        }

        public void Write(BinaryWriter w)
        {
            Inner.Write(w);
        }

        #region HierarchyId -> HierarchyId Operators

        public static SqlBoolean operator ==(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner == hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator !=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner != hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner < hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner > hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner <= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >=(HierarchyId hid1, HierarchyId hid2)
        {
            if (null != hid1 && !hid1.IsNull && null != hid2 && !hid2.IsNull)
            {
                return (hid1.Inner >= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        #endregion

        #region SqlHierarchyId -> HierarchyId  Operators

        public static SqlBoolean operator ==(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 == hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator !=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 != hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 < hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 > hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 <= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >=(SqlHierarchyId hid1, HierarchyId hid2)
        {
            if (!hid1.IsNull && hid2 != null && !hid2.IsNull)
            {
                return (hid1 >= hid2.Inner);
            }
            return SqlBoolean.Null;
        }

        #endregion

        #region HierarchyId -> SqlHierarchyId Operators

        public static SqlBoolean operator ==(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner == hid2);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator !=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner != hid2);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner < hid2);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner > hid2);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator <=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner <= hid2);
            }
            return SqlBoolean.Null;
        }

        public static SqlBoolean operator >=(HierarchyId hid1, SqlHierarchyId hid2)
        {
            if (hid1 != null && !hid1.IsNull && !hid2.IsNull)
            {
                return (hid1.Inner >= hid2);
            }
            return SqlBoolean.Null;
        }

        #endregion
    }
}